package com.yfp.client.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Response;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayPartnerRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.request.WxPayPartnerOrderCloseV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayPartnerUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.google.gson.Gson;
import com.hy.corecode.idgen.WFGIdGenerator;
import com.yfp.client.config.WxMpConfiguration;
import com.yfp.client.config.WxPayConfiguration;
import com.yfp.client.config.WxPayProperties;
import com.yfp.client.domain.*;
import com.yfp.client.domain.*;
import com.yfp.client.domain.dto.BalancePayDTO;
import com.yfp.client.domain.dto.CreateWxPayOrderDTO;
import com.yfp.client.domain.dto.StoredCalculateResultDTO;
import com.yfp.client.domain.dto.StoredResultsDTO;
import com.yfp.client.domain.vo.QrCodeInitVO;
import com.yfp.client.domain.vo.QrCodePayVO;
import com.yfp.client.enums.*;
import com.yfp.client.enums.*;
import com.yfp.client.mapper.ClientIntegralMapper;
import com.yfp.client.mapper.MallOrderDetailsMapper;
import com.yfp.client.mapper.MallOrderMapper;
import com.yfp.client.service.*;
import com.yfp.client.service.*;
import com.yfp.client.util.SecurityUtils;
import com.yfp.common.core.constant.CacheConstants;
import com.yfp.common.core.enums.PayStatusEnum;
import com.yfp.common.core.enums.PayTypeEnum;
import com.yfp.common.core.exception.ServiceException;
import com.yfp.common.core.utils.StringUtils;
import com.yfp.common.redis.service.RedisService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PayServiceImpl implements PayService {
    @Resource
    private WxPayProperties properties;
    @Resource
    private OrderComboService orderComboService;
    @Resource
    private RedisService redisService;
    @Resource
    private WFGIdGenerator wfgIdGenerator;
    @Autowired
    private Gson gson;
    @Resource
    private OrderService orderService;
    @Resource
    private PackOrderService packOrderService;
    @Resource
    private PositionService positionService;
    @Resource
    private PositionSharingService positionSharingService;
    @Resource
    private OrderListService orderListService;
    @Resource
    private OrderDetailService orderDetailService;
    @Autowired
    private WxMpConfiguration wxMpConfiguration;
    @Resource
    private DeviceCurrentComboStockService deviceCurrentComboStockService;
    @Resource
    private SysDictDataService sysDictDataService;
    @Resource
    private SysOperationLogService sysOperationLogService;
    @Resource
    private ClientService clientService;
    @Resource
    private ClientBillService clientBillService;
    @Resource
    private OrderGiftGoldService orderGiftGoldService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private MallProductService mallProductService;
    @Resource
    private MallOrderMapper mallOrderMapper;
    @Resource
    private MallOrderDetailsMapper mallOrderDetailsMapper;
    @Resource
    private MallOrderService mallOrderService;
    @Resource
    private ClientIntegralMapper clientIntegralMapper;

    /**
     * 创建订单
     *
     * @param openId
     * @param orderId
     * @return
     */
    @Override
    public WxPayUnifiedOrderV3Result.JsapiResult createOrder(String openId, String orderId) {

        // 获取订单信息
        OrderCombo orderCombo = orderComboService.getById(orderId);
        if (GoodsTypeEnum.ENTITY.getCode().equals(orderCombo.getGoodsType())) {
            Boolean existBreakage = sysOperationLogService.existBreakage(orderCombo.getMachineId());
            SysOperationLog sysOperationLog = sysOperationLogService.lastSyncPlan(orderCombo.getMachineId());
            if (null != sysOperationLog){
                boolean beforeOrEquals = DateUtil.date(orderCombo.getCreateTime()).isAfter(sysOperationLog.getCreartTime());
                if (existBreakage && beforeOrEquals) {
                    return null;
                }
            }
        }

        log.info("创建微信预支付订单openId:{},orderId:{}", openId, orderId);
        // // 获取订单信息
        // OrderCombo orderCombo = orderComboService.getById(orderId);
        String proceedsAccount;
        if (GoodsTypeEnum.ENTITY.getCode().equals(orderCombo.getGoodsType())) {
            // 获取点位信息
            Position position = positionService.listByMachineId(orderCombo.getMachineId());
            proceedsAccount = Optional.ofNullable(position.getProceedsAccount()).orElseGet(() -> sysDictDataService.getDefaultReceivingAccount());
        } else {
            proceedsAccount = sysDictDataService.getDefaultReceivingAccount();
        }

        // 创建交易单的参数对象
        WxPayPartnerUnifiedOrderV3Request wxPayPartnerUnifiedOrderV3Request = new WxPayPartnerUnifiedOrderV3Request();
        // 公众号id
        wxPayPartnerUnifiedOrderV3Request.setSpAppid(properties.getConfigs().get(1).getAppId());
        // 支付商户号
        wxPayPartnerUnifiedOrderV3Request.setSpMchId(properties.getConfigs().get(1).getMchId());
        // 收款商户号
        wxPayPartnerUnifiedOrderV3Request.setSubMchId(proceedsAccount);
        // 订单描述
        wxPayPartnerUnifiedOrderV3Request.setDescription("购买套餐");
        // 本系统订单号
        wxPayPartnerUnifiedOrderV3Request.setOutTradeNo(orderId);
        // 交易单过期时间
        wxPayPartnerUnifiedOrderV3Request.setTimeExpire(DateUtil.format(DateUtil.offsetMinute(orderCombo.getCreateTime(), 3), DatePattern.UTC_WITH_XXX_OFFSET_PATTERN));
        // 回调地址
        wxPayPartnerUnifiedOrderV3Request.setNotifyUrl(properties.getNotifyUrl().getCreate());

        // 实体商品参与分账
        if (GoodsTypeEnum.ENTITY.getCode().equals(orderCombo.getGoodsType())) {
            // 点位管理有创客（分账接收方），则指定分账
            if (positionSharingService.isExistSharing(orderId)) {
                WxPayPartnerUnifiedOrderV3Request.SettleInfo settleInfo = new WxPayPartnerUnifiedOrderV3Request.SettleInfo();
                settleInfo.setProfitSharing(true);
                wxPayPartnerUnifiedOrderV3Request.setSettleInfo(settleInfo);
            }
        }

        // 微信要求订单总金额，单位为分，所以要乘以100用来转换
        WxPayPartnerUnifiedOrderV3Request.Amount amount = new WxPayPartnerUnifiedOrderV3Request.Amount();
        amount.setTotal(NumberUtil.mul(orderCombo.getPrice(), "100").intValue());
        wxPayPartnerUnifiedOrderV3Request.setAmount(amount);

        // 支付者信息。sp_openid 和 sub_openid 两个字段必须要填一个
        WxPayPartnerUnifiedOrderV3Request.Payer payer = new WxPayPartnerUnifiedOrderV3Request.Payer();
        // openId   支付者微信唯一标识
        payer.setSpOpenid(openId);
        wxPayPartnerUnifiedOrderV3Request.setPayer(payer);
        // 订单附加信息
        wxPayPartnerUnifiedOrderV3Request.setAttach(WxPayAttach.getValue(orderCombo.getGoodsType()));
        // 创建交易单返回的实体类
        WxPayUnifiedOrderV3Result.JsapiResult partnerOrderV3 = null;
        try {
            // 校验  配置文件中的小程序商户是否存在
            WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
            // 创建交易单    参数：支付类型，基本参数
            partnerOrderV3 = mxPayService.createPartnerOrderV3(TradeTypeEnum.JSAPI, wxPayPartnerUnifiedOrderV3Request);
            // 添加进缓存，避免重复创建交易单
            redisService.setCacheObject(CacheConstants.MINAPP_ORDER_CREATE_WX_PAY_PARTNER_JSAPI_KEY + orderId, partnerOrderV3, 3L, TimeUnit.MINUTES);
            log.info("创建微信预支付订单成功创建微信预支付订单orderId:{},JsapiResult:{}", orderId, partnerOrderV3);
        } catch (WxPayException e) {
            log.error("创建微信预支付订单失败orderId:{}", orderId, e);
            return null;
        }

        // 插入支付订单
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        String wxPrepayId = StrUtil.replace(partnerOrderV3.getPackageValue(), "prepay_id=", "");
        orderCombo.setMerchantId(wxPayPartnerUnifiedOrderV3Request.getSubMchId());
        orderCombo.setPrepayId(wxPrepayId);
        orderCombo.setUpdateTime(timestamp);
        orderComboService.updateById(orderCombo);
        return partnerOrderV3;
    }

    /**
     * 支付回调
     *
     * @param xmlData
     * @return
     */
    @Override
    public String parseOrderNotifyResult(String xmlData) {
        log.info("支付回调xmlData:{}", xmlData);
        // 校验  配置文件中的小程序商户是否存在
        WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
        // 回调的返回实体类
        WxPayPartnerNotifyV3Result wxPayPartnerNotifyV3Result = null;
        try {
            // 获取回调返回的信息
            wxPayPartnerNotifyV3Result = mxPayService.parsePartnerOrderNotifyV3Result(xmlData, null);
        } catch (WxPayException e) {
            log.error("支付回调失败", e);
            return WxPayNotifyV3Response.fail("失败");
        }
        WxPayPartnerNotifyV3Result.DecryptNotifyResult result = wxPayPartnerNotifyV3Result.getResult();
        log.info("支付回调result:{}", result);
        // 校验返回的是成功还是失败
        switch (result.getTradeState()) {
            case WxPayConstants.WxpayTradeStatus.SUCCESS -> {
                try {
                    // 普通套餐购买
                    if (WxPayAttach.ENTITY_TYPE.getValue().equals(result.getAttach())) {
                        orderService.orderCreateNotify(wxPayPartnerNotifyV3Result);
                    }
                    // 虚拟商品，礼包购买
                    else if (WxPayAttach.VIRTUAL_PACK_TYPE.getValue().equals(result.getAttach())) {
                        packOrderService.orderCreateNotify(wxPayPartnerNotifyV3Result);
                    }
                    // 储值有礼购买
                    else if (WxPayAttach.STORED_VALUE_TYPE.getValue().equals(result.getAttach())) {
                        clientBillService.orderCreateNotify(wxPayPartnerNotifyV3Result);
                    }
                    //商城商品
                    else if (WxPayAttach.MALLPRODUCT_VALUE_TYPE.getValue().equals(result.getAttach())) {
                        mallOrderService.orderCreateNotify(wxPayPartnerNotifyV3Result);
                    }
                } catch (Exception e) {
                    log.error("支付回调失败", e);
                    return WxPayNotifyV3Response.fail("失败");
                }
            }
        }
        return WxPayNotifyV3Response.success("成功");
    }

    /**
     * 整单退款
     *
     * @param orderCombo 订单详情
     * @return
     */
    @Override
    public Boolean refund(OrderCombo orderCombo) {
        Position position = positionService.listByMachineId(orderCombo.getMachineId());
        String proceedsAccount = Optional.ofNullable(position.getProceedsAccount()).orElseGet(() -> sysDictDataService.getDefaultReceivingAccount());
        // 退款参数对象
        WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
        // 收款商户号
        wxPayRefundV3Request.setSubMchid(proceedsAccount);
        // 原订单号
        wxPayRefundV3Request.setOutTradeNo(orderCombo.getOrderId());
        // 整单退款OutRefundNo 使用 OrderId
        wxPayRefundV3Request.setOutRefundNo(orderCombo.getOrderId());
        // 退款回调地址
        wxPayRefundV3Request.setNotifyUrl(properties.getNotifyUrl().getRefund());
        // 订单金额信息
        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        // 需要退款金额
        amount.setRefund(NumberUtil.mul(orderCombo.getPrice(), "100").intValue());
        // 原订单交易总金额
        amount.setTotal(NumberUtil.mul(orderCombo.getPrice(), "100").intValue());
        // 币种
        amount.setCurrency(WxPayConstants.CurrencyType.CNY);
        wxPayRefundV3Request.setAmount(amount);

        try {
            // 校验  配置文件中的小程序商户是否存在
            WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
            WxPayRefundV3Result wxPayRefundV3Result = mxPayService.refundV3(wxPayRefundV3Request);
            log.info("退款成功,退款结果:{}", wxPayRefundV3Result);
            return true;
        } catch (WxPayException e) {
            log.error("退款失败", e);
        }
        return false;
    }

    /**
     * 退款回调
     *
     * @param xmlData
     * @return
     */
    @Override
    public String parseRefundNotifyResult(String xmlData) {
        log.info("退款回调xmlData:{}", xmlData);
        WxPayPartnerRefundNotifyV3Result wxPayPartnerRefundNotifyV3Result = null;
        try {
            WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
            wxPayPartnerRefundNotifyV3Result = mxPayService.parsePartnerRefundNotifyV3Result(xmlData, null);
        } catch (WxPayException e) {
            log.error("退款回调失败", e);
            return WxPayNotifyV3Response.fail("失败");
        }
        WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult result = wxPayPartnerRefundNotifyV3Result.getResult();
        switch (result.getRefundStatus()) {
            case WxPayConstants.RefundStatus.SUCCESS ->
                    orderService.orderRefundNotify(wxPayPartnerRefundNotifyV3Result);
        }
        return WxPayNotifyV3Response.success("成功");
    }

    /**
     * 关闭订单
     *
     * @param orderId
     */
    @Override
    public void closeOrder(String orderId) {
        log.info("关闭订单,orderId:{}", orderId);
        OrderCombo orderCombo = orderComboService.getById(orderId);
        Position position = positionService.listByMachineId(orderCombo.getMachineId());
        String proceedsAccount = Optional.ofNullable(position.getProceedsAccount()).orElseGet(() -> sysDictDataService.getDefaultReceivingAccount());

        WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
        WxPayPartnerOrderCloseV3Request wxPayPartnerOrderCloseV3Request = new WxPayPartnerOrderCloseV3Request();
        wxPayPartnerOrderCloseV3Request.setOutTradeNo(orderId);
        wxPayPartnerOrderCloseV3Request.setSpMchId(properties.getConfigs().get(1).getMchId());
        wxPayPartnerOrderCloseV3Request.setSubMchId(proceedsAccount);
        try {
            mxPayService.closePartnerOrderV3(wxPayPartnerOrderCloseV3Request);
        } catch (WxPayException e) {
            log.error("关闭订单失败,orderId:{}", orderId, e);
        }
    }

    /**
     * 获取支付二维码
     *
     * @param sessionId
     * @return
     */
    @Override
    public QrCodeInitVO qrCodePayPageInit(String sessionId) {

        OrderCombo orderCombo = orderComboService.getById(sessionId);
        long between = DateUtil.between(orderCombo.getCreateTime(), new Date(), DateUnit.MINUTE);
        Assert.isFalse(between > 3, "二维码已经过期");
        Assert.isFalse(orderCombo.getStatus().equals(OrderStatus.AwaitPackage.getStatus()), "订单已支付");

        String appId = properties.getConfigs().get(2).getAppId();

        QrCodeInitVO qrCodeVO = new QrCodeInitVO();
        qrCodeVO.setAppId(appId);
        qrCodeVO.setPrePayId(sessionId);

        return qrCodeVO;
    }

    /**
     * 获取支付信息
     *
     * @param wxCode
     * @param prePayId
     * @return
     */
    @Override
    public QrCodePayVO qrCodePayPagePayInfo(String wxCode, String prePayId) {
        WxMpService wxMpService = null;
        WxOAuth2AccessToken wxOAuth2AccessToken = null;
        try {
            wxMpService = wxMpConfiguration.wxMpService();
            wxOAuth2AccessToken = wxMpService.getOAuth2Service().getAccessToken(wxCode);
            log.info("wxOAuth2AccessToken: {}", JSONObject.toJSONString(wxOAuth2AccessToken));
        } catch (WxErrorException e) {
            log.error("获取openId失败", e);
            return null;
        }
        OrderCombo orderCombo = orderComboService.getById(prePayId);
        orderCombo.setUnionId(wxOAuth2AccessToken.getUnionId());
        orderComboService.updateById(orderCombo);

        QrCodePayVO qrCodePayVO = new QrCodePayVO();
        qrCodePayVO.setOpenId(wxOAuth2AccessToken.getOpenId());
        qrCodePayVO.setSubject("购买套餐");
        qrCodePayVO.setAmountOfMoney(orderCombo.getPrice());
        return qrCodePayVO;
    }

    /**
     * 管理员退款
     *
     * @param orderListId
     * @param amt
     */
    @Override
    public void adminRefund(String orderListId, BigDecimal amt) {
        log.info("管理员退款,orderListId:{}", orderListId);
        OrderList orderList = orderListService.getById(orderListId);
        OrderCombo orderCombo = orderComboService.getById(orderList.getOrderSn());
        Position position = positionService.listByMachineId(orderList.getMachineId());
        String proceedsAccount = "";
        if (Objects.isNull(position) || Objects.isNull(position.getProceedsAccount())) {
            proceedsAccount = sysDictDataService.getDefaultReceivingAccount();
        } else {
            proceedsAccount = position.getProceedsAccount();
        }

        Boolean isBabyCouponOrder = orderService.isBabyCouponOrder(orderCombo.getCouponId());
        if (!isBabyCouponOrder) {
            if (!NumberUtil.isIn(amt, NumberUtil.toBigDecimal("0.01"), NumberUtil.toBigDecimal(orderList.getAmt()))) {
                throw new ServiceException(StringUtils.format("退款金额不合规"));
            }

            WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
            wxPayRefundV3Request.setSubMchid(proceedsAccount);
            wxPayRefundV3Request.setOutTradeNo(orderList.getOrderSn());
            // 部分退款OutRefundNo 使用 Id
            // 商户退款单号 orderList:Id 等于 orderDetail:orderDetailId
            wxPayRefundV3Request.setOutRefundNo(orderList.getId());
            wxPayRefundV3Request.setNotifyUrl(properties.getNotifyUrl().getRefund());
            // 订单金额信息
            WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
            amount.setRefund(NumberUtil.mul(amt, NumberUtil.toBigDecimal("100")).intValue());
            amount.setTotal(NumberUtil.mul(orderCombo.getPrice(), "100").intValue());
            amount.setCurrency(WxPayConstants.CurrencyType.CNY);
            wxPayRefundV3Request.setAmount(amount);

            try {
                WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
                WxPayRefundV3Result wxPayRefundV3Result = mxPayService.refundV3(wxPayRefundV3Request);
                log.info("退款成功,退款结果:{}", wxPayRefundV3Result);
            } catch (WxPayException e) {
                log.error("退款失败", e);
                throw new ServiceException(e.getCustomErrorMsg());
            }
        }
        // 当天已付款状态后台退款，恢复退款套餐的设备库存
        OrderList orderLists = orderListService.getById(orderListId);
        if (PayStatusEnum.PAID.getCode() == orderLists.getPayStatus() && DateUtil.isSameDay(orderLists.getOrderTime(), new Date())) {
            log.info("当天已付款状态后台退款，恢复退款套餐的设备库存:{}", orderListId);
            OrderDetail orderDetail = orderDetailService.listByOrderDetailId(orderLists.getId());
            deviceCurrentComboStockService.recoverStockQuantity(orderCombo, Collections.singletonList(orderDetail));
        }
    }


    /**
     * 储值退款
     *
     * @param clientId
     */
    @Transactional
    public void storedRefund(String clientId) {

        // 获取充值记录
        List<ClientBill> cbList = clientBillService.balanceNotZero(clientId);

        try {
            for (ClientBill cb : cbList) {
                // 修改状态为待完成
                clientBillService.updateStatus(cb.getId(), 1);
                WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
                wxPayRefundV3Request.setSubMchid(sysDictDataService.getDefaultReceivingAccount());
                wxPayRefundV3Request.setOutTradeNo(cb.getOrderSn());
                wxPayRefundV3Request.setOutRefundNo(String.valueOf(wfgIdGenerator.next()));
                wxPayRefundV3Request.setNotifyUrl(properties.getNotifyUrl().getStoredRefund());
                // 订单金额信息
                WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
                amount.setRefund(NumberUtil.mul(cb.getCurrentBillMoney().toString(), "100").intValue());
                amount.setTotal(NumberUtil.mul(cb.getMoney().toString(), "100").intValue());
                amount.setCurrency(WxPayConstants.CurrencyType.CNY);
                wxPayRefundV3Request.setAmount(amount);
                WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
                WxPayRefundV3Result wxPayRefundV3Result = mxPayService.refundV3(wxPayRefundV3Request);
                log.info("储值退款结果:{}", wxPayRefundV3Result);
                if (!wxPayRefundV3Result.getStatus().equals("PROCESSING")) {
                    // 修改状态为待完成
                    clientBillService.updateStatus(cb.getId(), 2);
                }
            }
        } catch (WxPayException e) {
            log.error("储值退款失败", e);
            throw new ServiceException(e.getCustomErrorMsg());
        }
    }

    /**
     * 储值退款回调
     *
     * @param xmlData
     * @return
     */
    @Override
    public String notifyStoredRefund(String xmlData) {
        log.info("储值退款回调xmlData:{}", xmlData);
        WxPayPartnerRefundNotifyV3Result wxPayPartnerRefundNotifyV3Result = null;
        try {
            WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
            wxPayPartnerRefundNotifyV3Result = mxPayService.parsePartnerRefundNotifyV3Result(xmlData, null);
        } catch (WxPayException e) {
            log.error("退款回调失败", e);
            return WxPayNotifyV3Response.fail("失败");
        }
        WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult result = wxPayPartnerRefundNotifyV3Result.getResult();
        log.info("回调返回数据:{}", result);
        switch (result.getRefundStatus()) {
            case WxPayConstants.RefundStatus.SUCCESS ->
                    clientService.storedRefundNotify(wxPayPartnerRefundNotifyV3Result);
        }
        return WxPayNotifyV3Response.success("成功");
    }


    /**
     * 商城订单退款
     * @param id
     */
    @Override
    @Transactional
    public void mallOrderRefund(Long id) {

        // TODO 退回积分是否需要加锁


        MallOrderDetails mod = mallOrderDetailsMapper.getByMallOrderId(id);
        MallOrder mallOrder = mallOrderMapper.selectById(id);
        // 销售类型为积分兑换  直接返还积分
        if (mod.getMallProductType().equals(MallProductTypeEnum.INTEGRAL.getCode())) {
            // 退回积分
            RLock lock = redissonClient.getLock(CacheConstants.CLIENT_INTEGRAL_LOCK_KEY + mallOrder.getClientId());
            try {
                boolean b = lock.tryLock(5, TimeUnit.SECONDS);
                Assert.isTrue(b, "稍后重试");
                clientService.returnIntegral(mallOrder.getClientId(), mod.getIntegral());
                Integer status = MallOrderStatusEnum.REFUND.getCode();
                // 订单状态为代发货时，退还库存
                if (mallOrder.getStatus().equals(MallOrderStatusEnum.WAIT_SHIPMENT.getCode())) {
                    // 状态退货退款
                    status = MallOrderStatusEnum.SALES_RETURN_AND_REFUND.getCode();
                    // 退库存
                    mallProductService.recoverStockQuantity(mallOrder);
                }
                mallOrderService.updateStatus(mallOrder.getOrderSn(), status);

                // 插入退款记录
                Client client = clientService.getById(mallOrder.getClientId());
                ClientIntegral clientIntegral = new ClientIntegral();
                clientIntegral.setClientId(client.getId());
                clientIntegral.setIntegral(mod.getIntegral().intValue());
                clientIntegral.setSurplusIntegral(client.getIntegral());
                clientIntegral.setStatus(2);
                clientIntegral.setSource(2);
                clientIntegral.setType(1);
                clientIntegral.setOrderId(mallOrder.getOrderSn());
                clientIntegralMapper.insert(clientIntegral);
            } catch (Exception e) {
                throw new ServiceException(e.getMessage());
            } finally {
                // 释放锁
                lock.unlock();
            }
        } else {
            try {
                WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
                wxPayRefundV3Request.setSubMchid(sysDictDataService.getDefaultReceivingAccount());
                wxPayRefundV3Request.setOutTradeNo(mallOrder.getOrderSn());
                wxPayRefundV3Request.setOutRefundNo(String.valueOf(wfgIdGenerator.next()));
                wxPayRefundV3Request.setNotifyUrl(properties.getNotifyUrl().getMallOrderRefund());
                // 订单金额信息
                WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
                amount.setRefund(NumberUtil.mul(mod.getPrice().toString(), "100").intValue());
                amount.setTotal(NumberUtil.mul(mod.getPrice().toString(), "100").intValue());
                amount.setCurrency(WxPayConstants.CurrencyType.CNY);
                wxPayRefundV3Request.setAmount(amount);
                WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
                WxPayRefundV3Result wxPayRefundV3Result = mxPayService.refundV3(wxPayRefundV3Request);
                log.info("商城订单退款结果:{}", wxPayRefundV3Result);
                if (wxPayRefundV3Result.getStatus().equals("PROCESSING")) {
                    mallOrderService.updateStatus(mallOrder.getOrderSn(), MallOrderStatusEnum.WAIT_REFUND.getCode());
                }
            } catch (WxPayException e) {
                log.error("商城订单退款失败", e);
                throw new ServiceException(e.getCustomErrorMsg());
            }
        }
    }


    /**
     * 商城订单退款回调通知处理
     * @param xmlData
     * @return
     */
    @Override
    public String notifyMallOrderRefund(String xmlData) {
        log.info("商城订单退款回调xmlData:{}", xmlData);
        WxPayPartnerRefundNotifyV3Result wxPayPartnerRefundNotifyV3Result = null;
        try {
            WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(1));
            wxPayPartnerRefundNotifyV3Result = mxPayService.parsePartnerRefundNotifyV3Result(xmlData, null);
        } catch (WxPayException e) {
            log.error("退款回调失败", e);
            return WxPayNotifyV3Response.fail("失败");
        }
        WxPayPartnerRefundNotifyV3Result.DecryptNotifyResult result = wxPayPartnerRefundNotifyV3Result.getResult();
        log.info("回调返回数据:{}", result);
        switch (result.getRefundStatus()) {
            case WxPayConstants.RefundStatus.SUCCESS ->
                    mallOrderService.mallOrderRefundNotify(wxPayPartnerRefundNotifyV3Result);
        }
        return WxPayNotifyV3Response.success("成功");
    }


    /**
     * 设备发起订单
     *
     * @param openId
     * @param orderId
     * @return
     */
    @Override
    public WxPayUnifiedOrderV3Result.JsapiResult createDeviceOrder(String openId, String orderId) {
        if (redisService.hasKey(CacheConstants.MINAPP_ORDER_CREATE_WX_PAY_PARTNER_JSAPI_KEY + orderId)) {
            Object cacheObject = redisService.getCacheObject(CacheConstants.MINAPP_ORDER_CREATE_WX_PAY_PARTNER_JSAPI_KEY + orderId);
            Gson gson1 = new Gson();
            WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = gson1.fromJson(gson1.toJson(cacheObject), WxPayUnifiedOrderV3Result.JsapiResult.class);
            return jsapiResult;
        }
        log.info("创建微信设备支付预支付订单openId:{},orderId:{}", openId, orderId);
        OrderCombo orderCombo2222 = orderComboService.getById(orderId);
        log.info("orderCombo：{}", orderCombo2222);
        String maId = orderCombo2222.getMachineId();
        log.info("orderCombo.getMachineId():{}", maId);
        Position position = positionService.listByMachineId(maId);
        String proceedsAccount = Optional.ofNullable(position.getProceedsAccount()).orElseGet(() -> sysDictDataService.getDefaultReceivingAccount());
        log.info("proceedsAccount：{}", proceedsAccount);
        WxPayPartnerUnifiedOrderV3Request wxPayPartnerUnifiedOrderV3Request = new WxPayPartnerUnifiedOrderV3Request();
        wxPayPartnerUnifiedOrderV3Request.setSpAppid(properties.getConfigs().get(2).getAppId());
        wxPayPartnerUnifiedOrderV3Request.setSpMchId(properties.getConfigs().get(2).getMchId());
        wxPayPartnerUnifiedOrderV3Request.setSubMchId(proceedsAccount);
        wxPayPartnerUnifiedOrderV3Request.setDescription("购买套餐");
        wxPayPartnerUnifiedOrderV3Request.setOutTradeNo(orderId);
        wxPayPartnerUnifiedOrderV3Request.setTimeExpire(DateUtil.format(DateUtil.offsetMinute(orderCombo2222.getCreateTime(), 3), DatePattern.UTC_WITH_XXX_OFFSET_PATTERN));
        wxPayPartnerUnifiedOrderV3Request.setNotifyUrl(properties.getNotifyUrl().getCreate());

        log.info("wxPayPartnerUnifiedOrderV3Request1：{}", wxPayPartnerUnifiedOrderV3Request);
        // 点位管理有创客（分账接收方），则指定分账
        if (positionSharingService.isExistSharing(orderId)) {
            WxPayPartnerUnifiedOrderV3Request.SettleInfo settleInfo = new WxPayPartnerUnifiedOrderV3Request.SettleInfo();
            settleInfo.setProfitSharing(true);
            wxPayPartnerUnifiedOrderV3Request.setSettleInfo(settleInfo);
        }
        // 微信要求订单总金额，单位为分，所以要乘以100用来转换
        WxPayPartnerUnifiedOrderV3Request.Amount amount = new WxPayPartnerUnifiedOrderV3Request.Amount();
        amount.setTotal(NumberUtil.mul(orderCombo2222.getPrice(), "100").intValue());
        wxPayPartnerUnifiedOrderV3Request.setAmount(amount);

        // 支付者信息。sp_openid 和 sub_openid 两个字段必须要填一个
        WxPayPartnerUnifiedOrderV3Request.Payer payer = new WxPayPartnerUnifiedOrderV3Request.Payer();
        payer.setSpOpenid(openId);
        wxPayPartnerUnifiedOrderV3Request.setPayer(payer);

        // 订单附加信息
        wxPayPartnerUnifiedOrderV3Request.setAttach(WxPayAttach.getValue(orderCombo2222.getGoodsType()));
        WxPayUnifiedOrderV3Result.JsapiResult partnerOrderV3 = null;
        try {
            log.info("wxPayPartnerUnifiedOrderV3Request2：{}", wxPayPartnerUnifiedOrderV3Request);
            log.info("properties.getConfigs():{}",properties.getConfigs().get(2));
            WxPayService mxPayService = WxPayConfiguration.getMxPayService(properties.getConfigs().get(2));
            partnerOrderV3 = mxPayService.createPartnerOrderV3(TradeTypeEnum.JSAPI, wxPayPartnerUnifiedOrderV3Request);
            log.info("mxPayService:{}",mxPayService);
            redisService.setCacheObject(CacheConstants.MINAPP_ORDER_CREATE_WX_PAY_PARTNER_JSAPI_KEY + orderId, partnerOrderV3, 3L, TimeUnit.MINUTES);
            log.info("创建微信预支付订单成功创建微信预支付订单orderId:{},JsapiResult:{}", orderId, partnerOrderV3);
        } catch (WxPayException e) {
            log.error("创建微信预支付订单失败orderId:{}", orderId, e);
            return null;
        }

        // 插入支付订单
        Timestamp timestamp = new Timestamp(System.currentTimeMillis());
        String wxPrepayId = StrUtil.replace(partnerOrderV3.getPackageValue(), "prepay_id=", "");
        orderCombo2222.setMerchantId(wxPayPartnerUnifiedOrderV3Request.getSubMchId());
        orderCombo2222.setPrepayId(wxPrepayId);
        orderCombo2222.setUpdateTime(timestamp);
        orderComboService.updateById(orderCombo2222);
        return partnerOrderV3;
    }

    @Override
    public void parseRefundNotifyResultSimulate(String orderListId, BigDecimal amt) {
        OrderList orderList = orderListService.getById(orderListId);
        OrderCombo orderCombo = orderComboService.getById(orderList.getOrderSn());

        Boolean isBabyCouponOrder = orderService.isBabyCouponOrder(orderCombo.getCouponId());
        // 模拟退款回调
        if (isBabyCouponOrder) {
            orderService.simulateWechatPayOrderRefundNotify(orderCombo);
        } else if (PayTypeEnum.BalancePay.getCode().toString().equals(orderList.getPayType())) {
            orderService.balancePayOrderRefundNotify(orderList, amt);
        }
    }

    @Override
    public WxPayUnifiedOrderV3Result.JsapiResult createWxJsapiPayOrder(CreateWxPayOrderDTO createWxPayOrderDTO) {
        WxPayProperties.Config payConfig = properties.getConfigs().get(1);

        WxPayPartnerUnifiedOrderV3Request wxPayPartnerUnifiedOrderV3Request = new WxPayPartnerUnifiedOrderV3Request();
        wxPayPartnerUnifiedOrderV3Request.setSpAppid(payConfig.getAppId());
        wxPayPartnerUnifiedOrderV3Request.setSpMchId(payConfig.getMchId());
        wxPayPartnerUnifiedOrderV3Request.setSubMchId(createWxPayOrderDTO.getSubMchId());
        wxPayPartnerUnifiedOrderV3Request.setDescription(createWxPayOrderDTO.getDescription());
        wxPayPartnerUnifiedOrderV3Request.setOutTradeNo(createWxPayOrderDTO.getOutTradeNo());
        wxPayPartnerUnifiedOrderV3Request.setTimeExpire(DateUtil.format(createWxPayOrderDTO.getTimeExpire(), DatePattern.UTC_WITH_XXX_OFFSET_PATTERN));
        wxPayPartnerUnifiedOrderV3Request.setNotifyUrl(properties.getNotifyUrl().getCreate());
        wxPayPartnerUnifiedOrderV3Request.setAttach(createWxPayOrderDTO.getAttach());

        // 微信要求订单总金额，单位为分，所以要乘以100用来转换
        WxPayPartnerUnifiedOrderV3Request.Amount amount = new WxPayPartnerUnifiedOrderV3Request.Amount();
        amount.setTotal(NumberUtil.mul(createWxPayOrderDTO.getTotalPrice(), new BigDecimal("100")).intValue());
        wxPayPartnerUnifiedOrderV3Request.setAmount(amount);

        // 支付者信息。sp_openid 和 sub_openid 两个字段必须要填一个
        WxPayPartnerUnifiedOrderV3Request.Payer payer = new WxPayPartnerUnifiedOrderV3Request.Payer();
        payer.setSpOpenid(createWxPayOrderDTO.getOpenid());
        wxPayPartnerUnifiedOrderV3Request.setPayer(payer);

        WxPayUnifiedOrderV3Result.JsapiResult partnerOrderV3 = null;
        try {
            WxPayService mxPayService = WxPayConfiguration.getMxPayService(payConfig);
            partnerOrderV3 = mxPayService.createPartnerOrderV3(TradeTypeEnum.JSAPI, wxPayPartnerUnifiedOrderV3Request);
            log.info("创建微信预支付订单成功。创建微信预支付订单orderId:{},JsapiResult:{}", createWxPayOrderDTO.getOutTradeNo(), partnerOrderV3);
        } catch (WxPayException e) {
            log.error("创建微信预支付订单失败。orderId:{}", createWxPayOrderDTO.getOutTradeNo(), e);
            throw new ServiceException(e.getMessage());
        }
        return partnerOrderV3;
    }

    @Override
    public void balancePay(BalancePayDTO balancePayDTO) {
        String clientId = SecurityUtils.getClientId();

        // 余额支付时判断是否存在支付密码
        if (!clientService.hasPayPassword()) {
            throw new ServiceException("请先设置支付密码");
        }

        Integer passwordFree = clientService.payPasswordFree();
        // 余额免密支付密码按钮关闭（验证支付密码后扣款）
        if (passwordFree == 0) {
            Client client = clientService.getById(clientId);
            if (Objects.isNull(balancePayDTO.getPassword())) {
                throw new ServiceException("请输入支付密码");
            }
            if (!SecurityUtils.matchesPassword(balancePayDTO.getPassword(), client.getPayPassword())) {
                throw new ServiceException("支付密码错误，请重新输入");
            }
        }

        orderService.balancePay(balancePayDTO);

        orderService.balancePayNext(balancePayDTO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public StoredCalculateResultDTO calculateBalancePay(BalancePayDTO balancePayDTO) {
        List<OrderList> orderLists = orderListService.listByOrderSn(balancePayDTO.getOrderId());
        List<ClientBill> clientBillList = clientBillService.getClientBillRechargeList();

        StoredCalculateResultDTO storedCalculateResultDTO = this.storedCalculate(orderLists, clientBillList);

        // 减少客户client
        Client client = clientService.balanceSub(storedCalculateResultDTO.getStoredActualResultsDTO().getMoney(), storedCalculateResultDTO.getStoredActualResultsDTO().getGiftMoney());
        // 新增消费余额账单
        clientBillService.createBalancePay(balancePayDTO.getOrderId(), storedCalculateResultDTO.getStoredActualResultsDTO(), storedCalculateResultDTO.getClientBills(), client);
        // 添加
        orderGiftGoldService.addOrderGiftGold(storedCalculateResultDTO.getOrderGiftGolds());

        return storedCalculateResultDTO;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void adminBalanceRefund(String orderListId, BigDecimal amt) {
        OrderList orderList = orderListService.getById(orderListId);
        if (!NumberUtil.isIn(amt, NumberUtil.toBigDecimal("0.01"), NumberUtil.toBigDecimal(orderList.getAmt()))) {
            throw new ServiceException(StringUtils.format("退款金额不合规"));
        }
        OrderCombo orderCombo = orderComboService.getById(orderList.getOrderSn());

        // 部分退款计算
        List<OrderGiftGold> allOrderGiftGolds = orderGiftGoldService.listByOrderId(orderList.getOrderSn());
        ClientBill lastRatio = orderGiftGoldService.lastRatio(allOrderGiftGolds);
        StoredResultsDTO orderStoredResultsDTO = this.orderStored(Collections.singletonList(lastRatio), amt);
        log.info("adminBalanceRefund:{},退款计算StoredResultsDTO:{}", orderListId, orderStoredResultsDTO);
        List<OrderGiftGold> orderGiftGolds = orderGiftGoldService.listByOrderListId(orderListId);
        List<OrderGiftGold> orderGiftGolds2 = orderGiftGoldService.calculatePartialRefund(orderGiftGolds, orderStoredResultsDTO);
        log.info("adminBalanceRefund:{},退款计算OrderGiftGold:{}", orderListId, orderGiftGolds2);

        // 更新client
        Client client = clientService.balanceRefundOrder(orderCombo.getUserId(), orderGiftGolds2);
        // 更新client_bill
        clientBillService.balanceRefundOrder(client, orderGiftGolds2);
        // 新增client_bill
        clientBillService.balanceRefundInsertOrder(orderCombo.getOrderId(), client, orderGiftGolds2);

        BigDecimal giftGold = NumberUtil.sub(orderList.getGiftGold(), orderGiftGolds2.stream().map(item -> item.getGiftGold()).reduce(BigDecimal.ZERO, BigDecimal::add));
        orderListService.updateGiftGoldById(orderListId, giftGold);

        // 当天已付款状态后台退款，恢复退款套餐的设备库存
        if (PayStatusEnum.PAID.getCode() == orderList.getPayStatus() && DateUtil.isSameDay(orderList.getOrderTime(), new Date())) {
            log.info("当天已付款状态后台退款，恢复退款套餐的设备库存:{}", orderListId);
            OrderDetail orderDetail = orderDetailService.listByOrderDetailId(orderList.getId());
            deviceCurrentComboStockService.recoverStockQuantity(orderCombo, Collections.singletonList(orderDetail));
        }
    }

    private StoredCalculateResultDTO storedCalculate(List<OrderList> orderLists, List<ClientBill> clientBillList) {
        String clientId = SecurityUtils.getClientId();
        BigDecimal totalConsumptionPrice = orderLists.stream().map(item -> NumberUtil.toBigDecimal(item.getAmt())).reduce(BigDecimal.ZERO, BigDecimal::add);
        log.info("calculateBalancePay:{},消费总价:{}", clientId, totalConsumptionPrice);
        // 查找要使用的bill
        List<ClientBill> applyClientBillList = this.determineBillToApply(clientBillList, totalConsumptionPrice);
        log.info("calculateBalancePay:{},查找要使用的bill:{}", clientId, applyClientBillList);
        // 计算要使用的总金额和赠金
        StoredResultsDTO orderStoredResultsDTO = this.orderStored(applyClientBillList, totalConsumptionPrice);
        log.info("calculateBalancePay:{},要使用的总金额和赠金:{}", clientId, orderStoredResultsDTO);
        // 将要使用的总金额和赠金分配到每个orderList中
        List<StoredResultsDTO> orderListStoredResultsDTOList = this.orderListStored(orderStoredResultsDTO, orderLists);
        List<StoredResultsDTO> storedResultsDTOS = BeanUtil.copyToList(orderListStoredResultsDTOList, StoredResultsDTO.class);
        log.info("calculateBalancePay:{},将要使用的总金额和赠金分配到每个orderList中:{}", clientId, orderListStoredResultsDTOList);
        List<OrderGiftGold> orderGiftGolds = this.hasApplyClientBill(applyClientBillList, orderListStoredResultsDTOList);
        log.info("calculateBalancePay:{},使用的OrderGiftGold:{}", clientId, orderGiftGolds);
        log.info("calculateBalancePay:{},使用完后的金额和赠金:{}", clientId, orderListStoredResultsDTOList);
        log.info("calculateBalancePay:{},bill使用完后:{}", clientId, applyClientBillList);

        // 实际使用的金额和赠金
        StoredResultsDTO storedActualResultsDTO = BeanUtil.toBean(orderStoredResultsDTO, StoredResultsDTO.class);
        storedActualResultsDTO.setMoney(orderGiftGolds.stream().map(OrderGiftGold::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add));
        storedActualResultsDTO.setGiftMoney(orderGiftGolds.stream().map(OrderGiftGold::getGiftGold).reduce(BigDecimal.ZERO, BigDecimal::add));
        log.info("calculateBalancePay:{},使用的OrderGiftGold:{}", clientId, storedActualResultsDTO);

        StoredCalculateResultDTO storedCalculateResultDTO = StoredCalculateResultDTO.builder()
                .storedResultsDTO(orderStoredResultsDTO)
                .storedActualResultsDTO(storedActualResultsDTO)
                .storedResultsDTOS(storedResultsDTOS)
                .orderGiftGolds(orderGiftGolds)
                .clientBills(applyClientBillList).build();
        log.info("calculateBalancePay:{},使用的storedCalculateResultDTO:{}", clientId, storedCalculateResultDTO);
        return storedCalculateResultDTO;
    }

    /**
     * 确定使用哪几条 ClientBill
     *
     * @param clientBillList        currentBillMoney、currentBillGiftGold大于0的列表
     * @param totalConsumptionPrice 订单总价格
     * @return 需要使用的bill
     */
    private List<ClientBill> determineBillToApply(List<ClientBill> clientBillList, BigDecimal totalConsumptionPrice) {
        clientBillList = clientBillList.stream().filter(item -> {
            boolean currentBillMoney = NumberUtil.isGreater(item.getCurrentBillMoney(), BigDecimal.ZERO);
            boolean currentBillGiftGold = NumberUtil.isGreater(item.getCurrentBillGiftGold(), BigDecimal.ZERO);
            return currentBillMoney || currentBillGiftGold;
        }).sorted(Comparator.comparing(ClientBill::getId)).collect(Collectors.toList());

        BigDecimal currentSum = BigDecimal.ZERO;
        List<ClientBill> clientBillResultList = new ArrayList<>();

        for (ClientBill clientBill : clientBillList) {
            currentSum = NumberUtil.add(currentSum, NumberUtil.add(clientBill.getCurrentBillMoney(), clientBill.getCurrentBillGiftGold()));
            clientBillResultList.add(clientBill);

            if (NumberUtil.isGreaterOrEqual(currentSum, totalConsumptionPrice)) {
                break;
            }
        }

        if (NumberUtil.isLess(currentSum, totalConsumptionPrice)) {
            throw new RuntimeException("账户余额不足");
        }

        return clientBillResultList;
    }

    /**
     * 使用最后一条的比例进行扣款计算
     *
     * @param applyClientBillList   需要使用到的 ClientBill
     * @param totalConsumptionPrice 订单总价格
     * @return 订单总扣减金额和赠金
     */
    private StoredResultsDTO orderStored(List<ClientBill> applyClientBillList, BigDecimal totalConsumptionPrice) {
        ClientBill clientBillLast = CollUtil.getLast(applyClientBillList);

        BigDecimal giftRatio = clientBillLast.getGiftRatio();
        BigDecimal giftMoney = NumberUtil.mul(giftRatio, totalConsumptionPrice).setScale(2, RoundingMode.DOWN);
        BigDecimal money = NumberUtil.sub(totalConsumptionPrice, giftMoney);

        return StoredResultsDTO.builder().money(money).giftMoney(giftMoney).build();
    }

    /**
     * 将要使用的总金额和赠金分配到每个orderList中
     *
     * @param orderStoredResultsDTO 要使用的总金额和赠金
     * @param orderLists            orderList列表
     */
    private List<StoredResultsDTO> orderListStored(StoredResultsDTO orderStoredResultsDTO, List<OrderList> orderLists) {
        BigDecimal allPrice = NumberUtil.add(orderStoredResultsDTO.getMoney(), orderStoredResultsDTO.getGiftMoney());

        List<StoredResultsDTO> storedResultsDTOS = orderLists.stream().map(item -> {
            StoredResultsDTO storedResultsDTO = new StoredResultsDTO();
            storedResultsDTO.setOrderListId(item.getId());
            storedResultsDTO.setOrderComboId(item.getOrderSn());

            BigDecimal giftRatio = NumberUtil.div(orderStoredResultsDTO.getGiftMoney(), allPrice).setScale(6, RoundingMode.UP);

            BigDecimal giftMoney = NumberUtil.mul(NumberUtil.toBigDecimal(item.getAmt()), giftRatio).setScale(2, RoundingMode.DOWN);
            BigDecimal money = NumberUtil.sub(NumberUtil.toBigDecimal(item.getAmt()), giftMoney);

            storedResultsDTO.setMoney(money);
            storedResultsDTO.setGiftMoney(giftMoney);
            return storedResultsDTO;
        }).collect(Collectors.toList());

        // 计算 collect 列表中所有 money 和 giftMoney 的总和
        BigDecimal totalMoney = storedResultsDTOS.stream().map(StoredResultsDTO::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalGiftMoney = storedResultsDTOS.stream().map(StoredResultsDTO::getGiftMoney).reduce(BigDecimal.ZERO, BigDecimal::add);

        // 比较总和与 resultsDTO 中的 money 和 giftMoney
        BigDecimal diffMoney = orderStoredResultsDTO.getMoney().subtract(totalMoney);
        BigDecimal diffGiftMoney = orderStoredResultsDTO.getGiftMoney().subtract(totalGiftMoney);

        // 如果存在偏差，调整最后一个条目的 money 或 giftMoney
        StoredResultsDTO last = CollUtil.getLast(storedResultsDTOS);
        last.setMoney(NumberUtil.add(last.getMoney(), diffMoney));
        last.setGiftMoney(NumberUtil.add(last.getGiftMoney(), diffGiftMoney));

        return storedResultsDTOS;
    }

    private List<OrderGiftGold> hasApplyClientBill(List<ClientBill> applyClientBillList, List<StoredResultsDTO> storedResultsDTOReqList) {
        return storedResultsDTOReqList.stream().map(item -> this.hasApplyClientBillCalculate(applyClientBillList, item)).flatMap(Collection::stream).collect(Collectors.toList());
    }

    private List<OrderGiftGold> hasApplyClientBillCalculate(List<ClientBill> applyClientBillList, StoredResultsDTO storedResultsDTO) {
        List<OrderGiftGold> orderGiftGolds = new ArrayList<>();
        applyClientBillList.stream().filter(item -> {
            boolean currentBillMoney = NumberUtil.isGreater(item.getCurrentBillMoney(), BigDecimal.ZERO);
            boolean currentBillGiftGold = NumberUtil.isGreater(item.getCurrentBillGiftGold(), BigDecimal.ZERO);
            return currentBillMoney || currentBillGiftGold;
        }).sorted(Comparator.comparing(ClientBill::getId)).forEach(clientBill -> {
            boolean moreThanZero = NumberUtil.isLessOrEqual(storedResultsDTO.getMoney(), BigDecimal.ZERO);
            boolean giftMoneyThanZero = NumberUtil.isLessOrEqual(storedResultsDTO.getGiftMoney(), BigDecimal.ZERO);
            if (moreThanZero && giftMoneyThanZero) {
                return;
            }

            ClientBill clientBillTemp = BeanUtil.toBean(clientBill, ClientBill.class);

            // 消费当前账单的赠金
            if (NumberUtil.isGreater(storedResultsDTO.getGiftMoney(), BigDecimal.ZERO)) {
                if (NumberUtil.isGreaterOrEqual(clientBill.getCurrentBillGiftGold(), storedResultsDTO.getGiftMoney())) {
                    clientBill.setCurrentBillGiftGold(NumberUtil.sub(clientBill.getCurrentBillGiftGold(), storedResultsDTO.getGiftMoney()));
                    storedResultsDTO.setGiftMoney(BigDecimal.ZERO);
                } else {
                    storedResultsDTO.setGiftMoney(NumberUtil.sub(storedResultsDTO.getGiftMoney(), clientBill.getCurrentBillGiftGold()));
                    clientBill.setCurrentBillGiftGold(BigDecimal.ZERO);
                }
            }

            // 消费当前账单的金额
            if (NumberUtil.isGreater(storedResultsDTO.getMoney(), BigDecimal.ZERO)) {
                if (NumberUtil.isGreaterOrEqual(clientBill.getCurrentBillMoney(), storedResultsDTO.getMoney())) {
                    clientBill.setCurrentBillMoney(NumberUtil.sub(clientBill.getCurrentBillMoney(), storedResultsDTO.getMoney()));
                    storedResultsDTO.setMoney(BigDecimal.ZERO);
                } else {
                    storedResultsDTO.setMoney(NumberUtil.sub(storedResultsDTO.getMoney(), clientBill.getCurrentBillMoney()));
                    clientBill.setCurrentBillMoney(BigDecimal.ZERO);
                }
            }

            boolean b = NumberUtil.isGreater(storedResultsDTO.getMoney(), BigDecimal.ZERO) || NumberUtil.isGreater(storedResultsDTO.getGiftMoney(), BigDecimal.ZERO);
            boolean b1 = NumberUtil.isGreater(clientBill.getCurrentBillMoney(), BigDecimal.ZERO) || NumberUtil.isGreater(clientBill.getCurrentBillGiftGold(), BigDecimal.ZERO);
            if (b && b1) {

                // 消费当前账单的金额
                if (NumberUtil.isGreater(storedResultsDTO.getGiftMoney(), BigDecimal.ZERO)) {
                    if (NumberUtil.isGreaterOrEqual(clientBill.getCurrentBillMoney(), storedResultsDTO.getGiftMoney())) {
                        clientBill.setCurrentBillMoney(NumberUtil.sub(clientBill.getCurrentBillMoney(), storedResultsDTO.getGiftMoney()));
                        storedResultsDTO.setGiftMoney(BigDecimal.ZERO);
                    } else {
                        storedResultsDTO.setGiftMoney(NumberUtil.sub(storedResultsDTO.getGiftMoney(), clientBill.getCurrentBillMoney()));
                        clientBill.setCurrentBillMoney(BigDecimal.ZERO);
                    }
                }

                // 消费当前账单的赠金
                if (NumberUtil.isGreater(storedResultsDTO.getMoney(), BigDecimal.ZERO)) {
                    if (NumberUtil.isGreaterOrEqual(clientBill.getCurrentBillGiftGold(), storedResultsDTO.getMoney())) {
                        clientBill.setCurrentBillGiftGold(NumberUtil.sub(clientBill.getCurrentBillGiftGold(), storedResultsDTO.getMoney()));
                        storedResultsDTO.setMoney(BigDecimal.ZERO);
                    } else {
                        storedResultsDTO.setMoney(NumberUtil.sub(storedResultsDTO.getMoney(), clientBill.getCurrentBillGiftGold()));
                        clientBill.setCurrentBillGiftGold(BigDecimal.ZERO);
                    }
                }
            }

            OrderGiftGold orderGiftGold = OrderGiftGold.builder().orderListId(storedResultsDTO.getOrderListId()).orderId(storedResultsDTO.getOrderComboId()).clientBillId(clientBill.getId()).money(NumberUtil.sub(clientBillTemp.getCurrentBillMoney(), clientBill.getCurrentBillMoney())).giftGold(NumberUtil.sub(clientBillTemp.getCurrentBillGiftGold(), clientBill.getCurrentBillGiftGold())).build();
            orderGiftGolds.add(orderGiftGold);
        });
        return orderGiftGolds;
    }
}
